<?php

namespace Lib;

use R as R;

/**
 * RaffleHelper
 * 
 * The ultimate(maybe) solution to raffle
 * raffleresult[id, uid, reward, created_at] table must exist and used for keeping raffle results
 */
class RaffleHelper
{
    /**
     * 
     * @param User $user
     * @param integer|array $specificRewards 指定要抽的奖，数字或者数组，如果超过一个，也应该是连续的
     * @return null|RaffleResult null为未中奖，否则为奖项记录对象
     */
    public static function drawReward($user, $specificRewards = null)
    {
        if (!empty($specificRewards) && is_int($specificRewards)) {
            $specificRewards = [$specificRewards];
        }
        
        // 前多少名用户中奖概率不一样
        $config = [
            'chance' => 400,
            'chance_to' => 1000,
            'per_user' => 1, // 一个用户能中几个奖，0为无限制（无限制应该也需要限制一等奖等高奖项的中奖数量）
            'rewards' => [
                // 1就是一等奖，奖项更大，越往下应该越容易中，奖项必须是连续的，比如不能有1，3但是没有2
                1 => [
                    'limit' => 1 // 有多少奖是必须的
                ],
                2 => [
                    'limit' => 200
                ],
                3 => [
                    'limit' => 1000
                ]
            ]
        ];
        
        if (empty($config['chance']) || empty($config['chance_to']) || empty($config['rewards'])) {
            die('chance, chance_to and rewards must be configured');
        }
        
        $chance = $config['chance'];
        $chanceto = $config['chance_to'];
        
        if (count($specificRewards) == 1) {
            $specificReward = array_pop($specificRewards);
            if (isset($config['rewards'][$specificReward]['chance_specific']) && isset($config['rewards'][$specificReward]['chance_to_specific'])) {
                $chance = $config['rewards'][$specificReward]['chance_specific'];
                $chanceto = $config['rewards'][$specificReward]['chance_to_specific'];
            }
        }
        
        $rnd = mt_rand(1, $chanceto);
        // 没中奖
        if ($rnd > $chance) {
            return null;
        }
        
        $userRaffleResults = R::findAll('raffleresult', ' uid = ? ', [ $user->id ]);
        if (!empty($config['per_user']) && count($userRaffleResults) >= $config['per_user']) {
            // 用户已经达到个人中奖的上限
            return null;
        }
        // 用户具体的中奖情况，分别中了几个
        $userRewards = [];
        foreach ($userRaffleResults as $urr) {
            if (!isset($userRewards[$urr->reward])) {
                $userRewards[$urr->reward] = 0;
            }
            ++$userRewards[$urr->reward];
        }
        
        // 决定给用户中什么奖
        
        // 已经中出去的奖励数量
        $result = R::getAll('SELECT COUNT(id) as n, reward FROM raffleresult GROUP BY reward');
        $rewardsGiven = [];
        foreach ($result as $row) {
            $rewardsGiven[$row['reward']] = $row['n'];
        }
        
        // 只要有一个奖项设置了天
        $today = date('Y-m-d');
        if (!empty($specificRewards)) {
            $highestSpecificReward = min($specificRewards);
            if ($highestSpecificReward) {
                foreach ($config['rewards'] as $reward=>&$cfgr) {
                    if ($reward < $highestSpecificReward) {
                        $cfgr['limit'] = 0;
                        if (isset($cfgr[$today])) {
                            $cfgr[$today]['limit'] = 0;
                        }
                    }
                }
            }
        }
        
        // 可供抽奖的数量可能是总的也可能是这一天的（看是否设置了日期限制）
        $nbAvailableToRewards = [];
        $availableRewardsTotal = 0;
        ksort($config['rewards']);
        foreach ($config['rewards'] as $reward=>$rewardConfig) {
            if (isset($rewardConfig['per_user']) && isset($userRewards[$reward]) && $userRewards[$reward] >= $rewardConfig['per_user']) {
                // 用户该奖项已到上限
                $nbAvailableToRewards[$availableRewardsTotal] = $reward;
                continue;
            }
            // 先看时间无关的奖项限制
            $nbAvailableRewards = $rewardConfig['limit'];
            if (isset($rewardsGiven[$reward])) {
                $nbAvailableRewards -= $rewardsGiven[$reward];
            }
            // 如果设置了时间（天）限制
            if ($nbAvailableRewards > 0 && isset($rewardConfig[$today])) {
                // 因为奖项及抽奖的频次不会多，所以这里会为每次循环执行SQL请求，如果奖项数量很多，抽奖频次很高，需考虑优化
                $nbTodayRewarded = R::count('raffleresult', ' reward = ? AND DATE(created_at) = ? ', [ $reward, $today]);
                $todayNbAvailableRewards = max(0, $rewardConfig[$today]['limit'] - $nbTodayRewarded);
                $nbAvailableRewards = min($todayNbAvailableRewards, $nbAvailableRewards);
            }
            
            // 区间上限对应奖项，用于下面决定奖项
            $availableRewardsTotal += $nbAvailableRewards;
            $nbAvailableToRewards[$availableRewardsTotal] = $reward;
        }
        // 全没奖了，也不用继续了
        if ($availableRewardsTotal == 0) {
            return null;
        }
        
        $rnd2 = mt_rand(1, $availableRewardsTotal);
        $theReward = 0;
        foreach ($nbAvailableToRewards as $nbAvailable=>$reward) {
            if ($rnd2 <= $nbAvailable) {
                $theReward = $reward;
                break;
            }
        }
        
        if (!empty($specificRewards) && !in_array($theReward, $specificRewards)) {
            return null;
        }
        
        // 真的真的中奖了！！
        $rr = R::dispense('raffleresult');
        $rr->uid       = $user->id;
        $rr->reward    = $theReward;
        $rr->createdAt = R::isoDateTime();
        R::store($rr);
        
        return $rr;
    }
}